package com.yummy.cloud.gateway.route;

import java.net.URI;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.yummy.cloud.gateway.manage.dao.mapper.RouteDefinitionPlainMapper;
import com.yummy.cloud.gateway.manage.dao.model.RouteDefinitionPlain;
import com.yummy.cloud.gateway.manage.dao.model.RouteDefinitionPlainExample;
import com.yummy.cloud.gateway.manage.dao.model.RouteDefinitionPlainExample.Criteria;
import com.yummy.cloud.gateway.manage.dto.RouteDefinitionQuery;
import com.yummy.convention.exception.BusinessException;
import com.yummy.convention.page.Pagination;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class MysqlRouteDefinitionRepository implements RouteDefinitionRepository{
	
	private RouteDefinitionPlainMapper routeDefinitionPlainMapper;
	
	public MysqlRouteDefinitionRepository(RouteDefinitionPlainMapper routeDefinitionPlainMapper) {
		this.routeDefinitionPlainMapper = routeDefinitionPlainMapper;
	}
	
	@Override
	public Flux<RouteDefinition> getRouteDefinitions() {
		RouteDefinitionPlainExample example = new RouteDefinitionPlainExample();
		example.setOrderByClause("`order` asc");
		return Flux.defer(() -> {
			return Flux.fromIterable(routeDefinitionPlainMapper.selectByExample(example))
					.switchIfEmpty(Flux.empty())
					.map(r -> plainToSmart(r));
		});
	}

	private RouteDefinition plainToSmart(RouteDefinitionPlain plain) {
		RouteDefinition route = new RouteDefinition();
		route.setFilters(JSON.parseArray(plain.getFilters(),FilterDefinition.class));
		route.setId(plain.getId());
		route.setMetadata(JSON.parseObject(plain.getMetadata()));
		route.setOrder(plain.getOrder());
		route.setPredicates(JSON.parseArray(plain.getPredicates(), PredicateDefinition.class));
		route.setUri(URI.create(plain.getUri()));
		return route;
	}

	@Override
	public Mono<Void> save(Mono<RouteDefinition> route) {
		return route.flatMap(r -> {
			if (StringUtils.isEmpty(r.getId())) {
				return Mono.error(new IllegalArgumentException("id may not be empty"));
			}
			save(r);
			return Mono.empty();
		});
	}

	private RouteDefinitionPlain smartToPlain(RouteDefinition routeDefinition) {
		RouteDefinitionPlain plain = new RouteDefinitionPlain();
		plain.setFilters(JSON.toJSONString(routeDefinition.getFilters()));
		plain.setId(routeDefinition.getId());
		plain.setMetadata(JSON.toJSONString(routeDefinition.getMetadata()));
		plain.setOrder(routeDefinition.getOrder());
		plain.setPredicates(JSON.toJSONString(routeDefinition.getPredicates()));
		plain.setUri(routeDefinition.getUri().toString());
		return plain;
	}

	@Override
	public Mono<Void> delete(Mono<String> routeId) {
		return routeId.flatMap(id -> {
			int row = delete(id);
			if(row > 0) {
				return Mono.empty();
			}
			return Mono.defer(() -> Mono.error(
					new NotFoundException("RouteDefinition not found: " + id)));
		});
	}

	public int delete(String routeId) {
		return routeDefinitionPlainMapper.deleteByPrimaryKey(routeId);
	}

	public Pagination<RouteDefinition> getCurrentRouteDefinitions(RouteDefinitionQuery query) {
		List<RouteDefinitionPlain> list = getFromMysql(query);
		if(list == null) {
			return Pagination.empty(0);
		}
		long total = ((Page<RouteDefinitionPlain>)list).getTotal();
		if(list.size() == 0) {
			return Pagination.empty(total);
		}
		List<RouteDefinition> finalList = list.stream().map(r -> plainToSmart(r)).collect(Collectors.toList());
		return Pagination.create(total, finalList);
	}

	private List<RouteDefinitionPlain> getFromMysql(RouteDefinitionQuery query) {
		PageHelper.startPage(query.getPageNum(),query.getPageSize());
		RouteDefinitionPlainExample example = new RouteDefinitionPlainExample();
		Criteria criteria = example.createCriteria();
		example.setOrderByClause("`order` asc");
		if(!StringUtils.isEmpty(query.getRouteId())) {
			criteria.andIdLike("%"+query.getRouteId()+"%");
		}
		return routeDefinitionPlainMapper.selectByExample(example);
	}

	public int save(RouteDefinition route) {
		RouteDefinitionPlain plain = smartToPlain(route);
		int row = routeDefinitionPlainMapper.updateByPrimaryKey(plain);
		if(row <= 0) {
			row = routeDefinitionPlainMapper.insert(plain);
		}
		return row;
	}

	public int update(RouteDefinition route) {
		RouteDefinitionPlain plain = smartToPlain(route);
		int row = routeDefinitionPlainMapper.updateByPrimaryKey(plain);
		if(row <= 0) {
			throw new BusinessException("找不到路由配置，id="+route.getId());
		}
		return row;
	}

	public int create(RouteDefinition route) {
		RouteDefinitionPlain plain = smartToPlain(route);
		return routeDefinitionPlainMapper.insert(plain);
	}

}